using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace ModWatch
{

	/// <summary>
	/// A <see cref="ModWatch.ModWatcherExtenderBase"/> control
	/// that tracks changes to a <see cref="System.Web.UI.WebControls.ListBox"/>.
	/// </summary>
	[TargetControlType(typeof(ListBox))]
	public class ListBoxModWatcherExtender : ModWatcherExtenderBase
	{

		/// <summary>
		/// The selected values of the targeted
		/// <see cref="System.Web.UI.WebControls.ListBox"/>
		/// at the time that it was last saved.
		/// </summary>
		private List<string> SavedValue
		{
			get { return (List<string>)ViewState["SavedValue"]; }
			set { ViewState["SavedValue"] = value; }
		}

		/// <summary>
		/// Raises the <see cref="System.Web.UI.Control.Init"/> event.
		/// </summary>
		/// <param name="e">
		/// An <see cref="System.EventArgs"/>
		/// object that contains event data.
		/// </param>
		protected override void OnInit(EventArgs e)
		{
			base.OnInit(e);

			this.GettingScriptReferences += new EventHandler<ScriptReferencesEventArgs>(this_GettingScriptReferences);
		}

		/// <summary>
		/// Returns a value indicating whether the value of the targeted
		/// <see cref="System.Web.UI.WebControls.ListBox"/> has changed since it was last saved.
		/// </summary>
		/// <returns>true if a change has been detected, false otherwise.</returns>
		protected override bool HasBeenChanged()
		{
			List<string> currValues = GetCurrentSelection(TargetControl as ListBox);

			if (!IsListSubset(currValues, SavedValue))
				return true;
			if (!IsListSubset(SavedValue, currValues))
				return true;

			return false;
		}

		/// <summary>
		/// Saves the current value of the targeted
		/// <see cref="System.Web.UI.WebControls.ListBox"/>.
		/// </summary>
		public override void SaveTargetState()
		{
			SavedValue = GetCurrentSelection(TargetControl as ListBox);
		}

		/// <summary>
		/// Returns a list of currently selected list values.
		/// </summary>
		/// <param name="List">
		///	The <see cref="ListBox"/> whose values should be returned.
		/// </param>
		/// <returns>The list of currently selected list values.</returns>
		private List<string> GetCurrentSelection(ListBox List)
		{
			List<string> list = new List<string>();
			foreach (ListItem item in List.Items)
			{
				if (item.Selected)
				{
					if (!list.Contains(item.Value))
						list.Add(item.Value);
				}
			}
			return list;
		}

		/// <summary>
		/// Returns a value indicating whether one
		/// <see cref="List&lt;T>"/> is a subset of another.
		/// </summary>
		/// <typeparam name="T">
		/// The type of lists to be compared.
		/// </typeparam>
		/// <param name="Superset">
		///	The list to be checked to contain the Subset.
		/// </param>
		/// <param name="Subset">
		/// The list to be checked as the subset.
		/// </param>
		/// <returns>true if Subset is a subset of Superset, false otherwise.</returns>
		private bool IsListSubset<T>(List<T> Superset, List<T> Subset)
		{
			foreach (T item in Subset)
			{
				if (!Superset.Contains(item))
					return false;
			}
			return true;
		}

		/// <summary>
		/// Returns the <see cref="System.Web.UI.ScriptDescriptor"/>s required by
		/// this instance of <see cref="ModWatch.ListBoxModWatcherExtender"/>.
		/// </summary>
		/// <param name="targetControl">
		/// The <see cref="System.Web.UI.WebControls.ListBox"/> control being targeted.
		/// </param>
		/// <returns>
		/// A collection of <see cref="System.Web.UI.ScriptDescriptor"/>s
		/// defining this instance of <see cref="ModWatch.ListBoxModWatcherExtender"/>.
		/// </returns>
		protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors(Control targetControl)
		{
			ScriptBehaviorDescriptor sbd = new ScriptBehaviorDescriptor("ModWatch.ListBoxModWatcherBehavior", targetControl.ClientID);
			sbd.AddProperty("modGroup", this.ModGroup);
			sbd.AddProperty("valuesToCompare", this.SavedValue);

			return new ScriptDescriptor[] { sbd };
		}

		/// <summary>
		/// Registers the scripts needed by the
		/// <see cref="ModWatch.ListBoxModWatcherExtender"/> class.
		/// </summary>
		/// <param name="sender">
		/// The <see cref="System.Object"/> that fired the
		/// <see cref="ModWatch.ModWatchExtenderBase.GettingScriptReferences"/> event.
		/// </param>
		/// <param name="e">
		/// A <see cref="ModWatch.ScriptReferencesEventArgs"/> object that contains the event data.
		/// </param>
		private void this_GettingScriptReferences(object sender, ScriptReferencesEventArgs e)
		{
			e.AddWebResourceScriptReference("ModWatch.Scripts.ListBoxModWatcherBehavior.js");
		}

	}

}
